import Axios from "axios";
import type { AxiosInstance, AxiosRequestConfig } from "axios";
import type {
  RequestMethods,
  AxiosHttpResponse,
  AxiosHttpRequestConfig
} from "./types.d";
import qs from "qs";
import NProgress from "../progress";
import router from '@/router';
import {errorMessage} from "@/utils/message";
import { removeToken,getToken,setToken } from "@/utils/auth";
import { storageSession } from "@/utils/storage";
import { refreshToken } from "@/api/user";
import { startLoading, endLoading } from '@/utils/loading';
import { ElMessageBox } from 'element-plus';

// 详细配置可参考：www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
  baseURL: "",
  timeout: 10000,
  headers: {
    Accept: "application/json, text/plain, */*",
    "Content-Type": "application/json",
    "X-Requested-With": "XMLHttpRequest"
  },
  // get请求中数组格式的参数进行序列化
  paramsSerializer: params => qs.stringify(params, { indices: false })
};

// 是否正在刷新的标记
let isRefreshing = false
// 重试队列，每一项将是一个待执行的函数形式
let requests:any = []

class AxiosHttp {
  constructor(){
    this.httpInterceptorsRequest();
    this.httpInterceptorsResponse();
  }
  // 初始化配置对象
  private static initConfig: AxiosHttpRequestConfig = { headers: {} };
  // 保存当前Axios实例对象
  private static axiosInstance: AxiosInstance = Axios.create(defaultConfig);
  // 设置请求前的回调
  private beforeRequestCallback: AxiosHttpRequestConfig["beforeRequestCallback"] = undefined;
  // 设置响应前的回调
  private beforeResponseCallback: AxiosHttpRequestConfig["beforeResponseCallback"] = undefined;

  private static transformConfigByMethod(
    params: any,
    config: AxiosHttpRequestConfig
  ): AxiosHttpRequestConfig {
    const { method } = config;
    const props = ["delete", "get", "head", "options"].includes(
      method!.toLocaleLowerCase()
    )
      ? "params"
      : "data";
    return {
      ...config,
      [props]: params
    };
  }
  // 请求拦截
  private httpInterceptorsRequest(): void {
    AxiosHttp.axiosInstance.interceptors.request.use(
      (config: AxiosHttpRequestConfig) => {
        const $config = config;
        console.log($config.config,"请求拦截");
        if($config.config.showLoading){
          startLoading($config.config.loadingStyle);
        }
        // 断网提示
        if (!navigator.onLine) {
            ElMessageBox.alert(
            '您的网络故障，请检查!',
            '温馨提示',
            {
                confirmButtonText: '好的',
                type: 'warning'
            }
            )
        }
        // 开启进度条动画
        NProgress.start();
        // 优先判断请求是否传入了自定义配置的回调函数，否则执行初始化设置等回调
        if (typeof config.beforeRequestCallback === "function") {
          config.beforeRequestCallback($config);
          this.beforeRequestCallback = undefined;
          return $config;
        }
        // 判断初始化状态中有没有回调函数，没有的话
        if (AxiosHttp.initConfig.beforeRequestCallback) {
          AxiosHttp.initConfig.beforeRequestCallback($config);
          return $config;
        }
        // 确保config.url永远不会是undefined，增加断言
        if(!config.url){
          config.url = ""
        }
        // 登录接口和刷新token接口不需要在headers中回传token，在走刷新token接口走到这里后，需要拦截，否则继续往下走，刷新token接口这里会陷入死循环
        if (config.url.indexOf('/refreshToken') >= 0 || config.url.indexOf('/login') >= 0) {
          return $config
        }
        // 回传token
        const token = getToken();
        // 判断token是否存在
        if (token) {
          const data = JSON.parse(token);
          // 确保config.headers永远不会是undefined，增加断言
          if(!config.headers){
            config.headers = {}
          }
          config.headers["Authorization"] = "Bearer " + data.accessToken;
          return $config;
        } else {
          return $config;
        }
      },
      error => {
        // 当前请求出错，当前请求加1的loading也需要减掉
        if(error.config.config.showLoading){
          endLoading();
        }
        return Promise.reject(error);
      }
    );
  }

  // 响应拦截
  private httpInterceptorsResponse(): void {
    const instance = AxiosHttp.axiosInstance;
    instance.interceptors.response.use(
      (response: AxiosHttpResponse) => {
        console.log(response,"请求响应数据");
        const $config = response.config;
        // 关闭进度条动画
        NProgress.done();
        if (response.data.code == 200) {
          // 优先判断请求是否传入了自定义配置的回调函数，否则执行初始化设置等回调
          if (typeof $config.beforeResponseCallback === "function") {
            $config.beforeResponseCallback(response);
            this.beforeResponseCallback = undefined;
            return response.data;
          }
          if (AxiosHttp.initConfig.beforeResponseCallback) {
            AxiosHttp.initConfig.beforeResponseCallback(response);
            return response.data;
          }
        } else {
          if (response.data.code == 201002) {
            errorMessage("登录已超时，请重新登录！");
            // 清除cookie中的token
            removeToken();
            // 清除缓存中的用户信息
            storageSession.removeItem("userInfo");
            router.push(`/Login?redirect=${router.currentRoute.value.fullPath}`);
          }
          if (response.data.code == 201004) {
            const token = getToken();
            if(token){
              const data = JSON.parse(token);
              if(!isRefreshing){
                isRefreshing = true;
                return refreshToken({refreshToken:data.refreshToken}).then((res:any)=>{
                  if(res.status){
                    setToken(res.data);
                    // 确保config.headers永远不会是undefined，增加断言
                    if(!$config.headers){
                      $config.headers = {}
                    }
                    $config.headers['Authorization'] = "Bearer " + res.data.accessToken;
                    $config.baseURL = ''
                    // 已经刷新了token，将所有队列中的请求进行重试
                    requests.forEach((callback:any) => callback(token))
                    requests = [];
                    return instance($config);
                  }
                }).catch((res:any)=>{
                  // 清除cookie中的token
                  removeToken();
                  // 清除缓存中的用户信息
                  storageSession.removeItem("userInfo");
                  router.push(`/Login?redirect=${router.currentRoute.value.fullPath}`);
                }).finally(()=>{
                  isRefreshing = false;
                });
              }else{
                // 正在刷新token，将返回一个未执行resolve的promise
                return new Promise((resolve) => {
                  // 将resolve放进队列，用一个函数形式来保存，等token刷新后直接执行
                  requests.push((accessToken:string) => {
                    // 响应以后，请求接口的$config.url已经包含了baseURL部分，这里需要将baseURL清空，防止再次请求时，再次组装，导致url错误
                    $config.baseURL = '';
                    // 确保config.headers永远不会是undefined，增加断言
                    if(!$config.headers){
                      $config.headers = {}
                    }
                    $config.headers['Authorization'] = "Bearer " + accessToken;
                    resolve(instance($config));
                  })
                })
              }
            }
          }
        }
        if(response.config.config.showLoading){
          endLoading();
        }
        return response.data;
      },
      (error) => {
        const $error = error;
        if($error.config.config.showLoading){
          endLoading();
        }
        // 关闭进度条动画
        NProgress.done();
        // 请求超时处理
        if ($error.message.indexOf('timeout') !== -1) {
            ElMessageBox.alert(
            '请求超时，请重新刷新页面！',
            '温馨提示',
            {
                confirmButtonText: '好的',
                type: 'warning'
            }
            )
        }
        // 所有的响应异常 区分来源为取消请求/非取消请求
        return Promise.reject($error);
      }
    );
  }

  // 通用请求工具函数
  public request<T>(
    method: RequestMethods,
    url: string,
    param?: T,
    axiosConfig?: AxiosHttpRequestConfig
  ): Promise<T> {
    // post与get请求的参数需要用不同的key去接收，这里需要做判断，在请求中get的参数是params接收的，post的参数是data接收的
    const config = AxiosHttp.transformConfigByMethod(param, {
      method,
      url,
      headers: axiosConfig?.headers || defaultConfig.headers || {},
      ...axiosConfig
    } as AxiosHttpRequestConfig);
    // 如果没有自定义的loading配置，需要给默认值，默认值就是启用全局loading
    if(config.config===undefined){
      config.config={
        showLoading:true
      };
    }else{ // 防止定义了config，但未定义showLoading
      if(config.config.showLoading===undefined){
        config.config.showLoading = true;
      }
    }
    // 单独处理自定义请求/响应回调
    if (axiosConfig?.beforeRequestCallback) {
      this.beforeRequestCallback = axiosConfig.beforeRequestCallback;
    }
    if (axiosConfig?.beforeResponseCallback) {
      this.beforeResponseCallback = axiosConfig.beforeResponseCallback;
    }
    // 单独处理自定义请求/响应回掉
    return new Promise((resolve, reject) => {
      AxiosHttp.axiosInstance.request(config).then((response:any) => {
        resolve(response);
      }).catch(error => {
        reject(error);
      });
    });
  }

  // 单独抽离的post工具函数
  public post<T>(
    url: string,
    params?: T,
    config?: AxiosHttpRequestConfig
  ): Promise<T> {
    return this.request<T>("post", url, params, config);
  }

  // 单独抽离的get工具函数
  public get<T>(
    url: string,
    params?: T,
    config?: AxiosHttpRequestConfig
  ): Promise<T> {
    return this.request<T>("get", url, params, config);
  }
}

export const http = new AxiosHttp();